package main

import (
	"fmt"
	"github.com/afex/hystrix-go/hystrix"
	"github.com/gin-contrib/sessions"
	"github.com/gin-contrib/sessions/redis"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
	"github.com/spf13/viper"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/credentials/insecure"
	"google.golang.org/grpc/resolver"
	"myweb/web/auth/interceptor"
	"myweb/web/auth/tls"
	"myweb/web/conf"
	"myweb/web/controller"
	"myweb/web/dao/mysql"
	"myweb/web/discovery"
	"myweb/web/logger"
	"myweb/web/middleware"
	"myweb/web/model"
	"myweb/web/proto/getCaptcha"
	"myweb/web/proto/house"
	"myweb/web/proto/order"
	"myweb/web/proto/user"
	"net"
	"net/http"
	"time"
)

// 添加gin框架开发3步骤
func main() {

	// 加载配置文件
	err := conf.InitConfig()
	if err != nil {
		fmt.Println("配置文件初始化失败:", err)
		return
	}
	fmt.Println("配置文件初始化加载完毕。。。", conf.Conf)

	// 初始化日志器
	if err = logger.Init(conf.Conf.LogConfig, conf.Conf.Mode); err != nil {
		fmt.Println("init logger failed, err:", err)
		return
	}
	fmt.Println("init logger success...")

	// 初始化Redis连接池
	model.InitRedis()

	// 初始化mysql连接
	err = mysql.Init()
	if err != nil {
		fmt.Println("mysql连接初始化失败:", err)
		return
	}
	fmt.Println("mysql连接初始化完毕。。。")

	// 初始化证书认证
	credentials := tls.Init()
	if credentials == nil {
		fmt.Println("初始化证书认证失败:")
	}

	// 连接微服务==============================
	// etcd地址
	etcdAddr := conf.Conf.Etcd.Address

	// 服务名
	getCaptchaServiceName := viper.GetString("service.GetCaptcha")
	userServiceName := viper.GetString("service.User")
	////getAreaServiceName := viper.GetString("service.GetArea")
	houseServiceName := viper.GetString("service.House")
	orderServiceName := viper.GetString("service.Order")
	// 注册etcd解析器
	etcdResolver := discovery.NewResolver([]string{etcdAddr}, logrus.New())

	// grpc中注册ETCD服务发现解析器
	resolver.Register(etcdResolver)

	// 连接时长上下文
	ctx, _ := context.WithTimeout(context.Background(), 3*time.Second)

	// 连接 验证码微服务
	// TODO TLS协议支持？
	getCaptchaConn, err := RPCConnect(ctx, getCaptchaServiceName, etcdResolver.Scheme(), insecure.NewCredentials())
	if err != nil {
		fmt.Printf("服务:%v,RPCConnect连接建立失败,err:%v\n", getCaptchaServiceName, err)
	}
	fmt.Printf("服务:%v,RPCConnect连接建立完成\n", getCaptchaServiceName)
	//获得grpc句柄 生成GetCaptcha_service微服务操作客户端
	captchaClient := getCaptcha.NewGetCaptchaClient(getCaptchaConn)

	// 连接 用户相关微服务
	userConn, err := RPCConnect(ctx, userServiceName, etcdResolver.Scheme(), insecure.NewCredentials())
	if err != nil {
		fmt.Printf("服务:%v,RPCConnect连接建立失败,err:%v\n", userServiceName, err)
	}
	fmt.Printf("服务:%v,RPCConnect连接建立完成\n", userServiceName)
	//获得grpc句柄 生成GetCaptcha_service微服务操作客户端
	userClient := user.NewUserClient(userConn)

	// 连接 房屋微服务
	houseConn, err := RPCConnect(ctx, houseServiceName, etcdResolver.Scheme(), insecure.NewCredentials())
	if err != nil {
		fmt.Printf("服务:%v,RPCConnect连接建立失败,err:%v\n", getCaptchaServiceName, err)
	}
	fmt.Printf("服务:%v,RPCConnect连接建立完成\n", getCaptchaServiceName)
	//获得grpc句柄 生成GetCaptcha_service微服务操作客户端
	houseClient := house.NewHouseClient(houseConn)

	// 连接 订单微服务
	orderConn, err := RPCConnect(ctx, orderServiceName, etcdResolver.Scheme(), insecure.NewCredentials())
	if err != nil {
		fmt.Printf("服务:%v,RPCConnect连接建立失败,err:%v\n", orderServiceName, err)
	}
	fmt.Printf("服务:%v,RPCConnect连接建立完成\n", orderServiceName)
	//获得grpc句柄 生成GetCaptcha_service微服务操作客户端
	orderClient := order.NewOrderClient(orderConn)
	// 初始化路由
	router := gin.Default()
	router.Use(gin.Logger(), logger.GinLogger(), logger.GinRecovery(true), middleware.Cors())
	/*
	 这是一个用于处理跨域资源共享(CORS)的定制中间件功能。CORS是一种机制，它使用额外的HTTP头来告诉浏览器，让运行在一个源的web应用程序访问来自不同源的选定资源
	*/

	// 初始化容器
	store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("secret"))
	// 使用容器，中间件
	//router.Use(sessions.Sessions("mysession", store))

	// 全部微服务操作客户端 和session 载入到gin中
	router.Use(middleware.InitMiddleware([]interface{}{captchaClient, userClient, houseClient, orderClient}),
		sessions.Sessions("mysession", store))

	// 路由匹配
	//router.GET("/", func(ctx *gin.Context) {
	//	ctx.Writer.WriteString("项目开始了...")
	//})

	// 设置静态资源的直接访问路径URL和静态资源所在的根目录（访问路径 / 默认读取静态资源）
	router.Static("/home", "view")

	// 熔断服务
	middleware.NewServiceWrapper(getCaptchaServiceName)
	middleware.NewServiceWrapper(userServiceName)
	//middleware.NewServiceWrapper(getAreaServiceName)
	middleware.NewServiceWrapper(houseServiceName)
	middleware.NewServiceWrapper(orderServiceName)

	streamHandler := hystrix.NewStreamHandler()
	streamHandler.Start()
	go http.ListenAndServe(net.JoinHostPort("", "8081"), streamHandler)

	// 添加路由分组
	r1 := router.Group("/api/v1.0")
	{
		r1.GET("/session", controller.GetSession)
		r1.GET("/imagecode/:uuid", controller.GetImageCd)
		r1.GET("/smscode/:phone", controller.GetSmsCd)
		r1.POST("/users", controller.PostRet)
		r1.GET("/areas/", controller.GetArea)
		r1.POST("/sessions", controller.PostLogin)
		r1.DELETE("/session", controller.DeleteSession)
		r1.GET("/user", controller.GetUserInfo)
		r1.PUT("/user/name", controller.PutUserInfo)
		r1.POST("/user/avatar", controller.PostAvatar)
		r1.POST("user/auth", controller.PostUserAuth)
		r1.GET("/user/auth", controller.GetUserInfo)     // 获取用户实名信息 和获取用户信息一样的路由
		r1.GET("/user/houses", controller.GetUserHouses) // 获取用户房源信息
		r1.POST("/houses", controller.PostHouses)
		//添加房源图片
		r1.POST("/houses/:id/images", controller.PostHousesImage)
		// 展示房屋详情
		r1.GET("houses/:id", controller.GetHouseInfo)
		// 获取首页轮播图片服务
		r1.GET("/house/index", controller.GetIndex)
		//搜索房屋
		r1.GET("/houses", controller.GetHouses)

		// 订单相关
		r1.POST("/orders", controller.PostOrders)
		r1.GET("/user/orders", controller.GetOrders)

		// 接收订单/拒绝订单
		r1.PUT("/orders/:id/status", controller.PutOrders)
		// 订单评论
		// http://10.10.53.170:8080/api/v1.0/orders/10/comment
		r1.PUT("/orders/:id/comment", controller.PutComment)
	}

	// 启动运行
	router.Run("127.0.0.1:8080")
}

func RPCConnect(ctx context.Context, serviceName string, scheme string, credentials credentials.TransportCredentials) (conn *grpc.ClientConn, err error) {
	// 不能加author
	addr := fmt.Sprintf("%s:///%s", scheme, serviceName)

	/*
		    建立grpc连接
		    grpc.WithDefaultServiceConfig({"loadBalancingPolicy": "round_robin"}): 此选项将默认服务配置设置为使用轮循负载均衡策略。这意味着请求将在服务的所有可用实例中均匀分布。
			grpc.WithTransportCredentials(credentials): 此选项设置要用于连接的传输凭据。这些凭证用于对客户机和服务器进行身份验证，反之亦然，并用于加密通过连接发送的数据。
	*/
	conn, err = grpc.DialContext(ctx, addr, grpc.WithDefaultServiceConfig(`{"loadBalancingPolicy": "round_robin"}`), grpc.WithTransportCredentials(credentials),
		grpc.WithPerRPCCredentials(&interceptor.Authentication{
			User: "admin",
			Pwd:  "admin",
		}))

	return
}
